local class = require('middleclass')
local utils = require('utils.utils')
local List = class('List')

function List:initialize()
    self.__ = {}
end

function List:size()
    return #self.__
end

function List:append(item)
    if not utils.types.is_null(item) then
        self.__[self:size() + 1] = item
    end
end

function List:pop()
    if self:size() > 0 then
        return table.remove(self.__)
    end
end

function List:shift()
    self:remove(1)
end

function List:unshift(item)
    self:insert(item, 1)
end

function List:remove(pos)
    if pos > 0 and pos <= self:size() then
        table.remove(self.__, pos)
    end
end

function List:insert(item, pos)
    if not utils.types.is_null(item) and pos > 0 and pos <= self:size() then
        table.insert(self.__, pos, item)
    end
end

function List:swap(i, j)
    if not utils.types.is_null(self:at(i)) and not utils.types.is_null(self:at(j)) then
        self.__[i], self.__[j] = self.__[j], self.__[i]
    end
end

function List:move(from, offset)
    local iv = self:at(from)
    local pos = from + offset
    local size = self:size()
    if from > 0 and from <= size and pos > 0 and pos <= size then
        local min, max = offset, 1
        if offset > 0 then
            min, max = 1, offset
        end
        for i = min, max do
            self:swap(from + i - 1, from + i)
        end
    end
end

function List:clear()
    while next(self.__) do
        table.remove(self.__)
    end
end

function List:foreach(cb)
    if utils.types.is_func(cb) then
        for i, v in ipairs(self.__) do
            cb(i, v)
        end
    end
end

function List:filter(cb)
    if utils.types.is_func(cb) then
        for i, v in ipairs(self.__) do
            if cb(i, v) then
                return v, i
            end
        end
    end
end

function List:contains(v)
    local _, i =
        self:filter(
        function(_, _v)
            return _v == v
        end
    )
    return not utils.types.is_null(i)
end

function List:find(v)
    return self:filter(
        function(i, t)
            if t == v then
                return i
            end
        end
    )
end

function List:at(pos)
    if utils.types.is_number(pos) and pos > 0 and pos <= self:size() then
        return self.__[pos]
    end
end

function List:head()
    return self:at(1)
end

function List:tail()
    return self:at(self:size())
end

return List
